﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using OverStore.Runtime.Storage;
using OverStore.Runtime.OverStoreContext;
using OverStore.Configuration.Components;
using OverStore.Runtime.PersistenceServiceProvider;
using OverStore.Runtime.ObjectRepository.Mapping;
using OverStore.Runtime.ObjectRepository.IdentifierProvider;
using OverStore.Configuration.ObjectRepository.Reader;
using OverStore.Configuration.ObjectRepository.Mapping;

namespace OverStore.Configuration.Builder.Fluent
{
    /// <summary>
    /// Steps for creating parameterized read variant.
    /// </summary>
    /// <typeparam name="TInstance">The type of the instance.</typeparam>
    /// <typeparam name="TIdentifier">The type of the identifier.</typeparam>
    /// <typeparam name="TParameter">The type of the parameter.</typeparam>
    /// <typeparam name="TNextStep">The type of the next step.</typeparam>
    public class ParameterizedReaderBuilder<TInstance, TIdentifier, TParameter, TNextStep>
        where TParameter : class
        where TInstance : class
    {
        /// <summary>
        /// Parameters are required for creating parameterized read variant.
        /// </summary>
        public class Param
        {
            private Func<ParameterizedReaderConfiguration<TInstance, TIdentifier, TParameter>, TNextStep> _OnCompleted;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterizedReaderBuilder&lt;TInstance, TIdentifier, TParameter, TNextStep&gt;.Param"/> class.
            /// </summary>
            /// <param name="onCompleted">The on completed.</param>
            public Param(Func<ParameterizedReaderConfiguration<TInstance, TIdentifier, TParameter>, TNextStep> onCompleted)
            {
                if (Object.ReferenceEquals(onCompleted, null))
                    throw new ArgumentNullException("onCompleted");
                _OnCompleted = onCompleted;
            }

            /// <summary>
            /// Gets or sets a value indicating whether [use resultset for object construction].
            /// </summary>
            /// <value>
            /// 	<c>true</c> if [use resultset for object construction]; otherwise, <c>false</c>.
            /// </value>
            public bool UseResultsetForObjectConstruction { get; set; }

            /// <summary>
            /// Gets or sets the read command.
            /// </summary>
            /// <value>The read command.</value>
            public IComponentConfiguration<ICommand> ReadCommand { get; set; }

            /// <summary>
            /// Gets or sets the parameter mapper.
            /// </summary>
            /// <value>The parameter mapper.</value>
            public IComponentConfiguration<IMapper<TParameter>> ParameterMapper { get; set; }

            /// <summary>
            /// Gets or sets the identifier provider.
            /// </summary>
            /// <value>The identifier provider.</value>
            public IComponentConfiguration<IIdentifierProvider<TIdentifier>> IdentifierProvider { get; set; }

            /// <summary>
            /// Creates the PRV.
            /// </summary>
            /// <returns></returns>
            private ParameterizedReaderConfiguration<TInstance, TIdentifier, TParameter> CreateParameterizedReader()
            {
                return new ParameterizedReaderConfiguration<TInstance, TIdentifier, TParameter>
                {
                    IdentifierProvider = IdentifierProvider,
                    ParameterMapper = ParameterMapper,
                    ReadCommand = ReadCommand,
                    UseResultSetForObjectConstruction = UseResultsetForObjectConstruction
                };
            }

            /// <summary>
            /// Ends the parameterized read variant.
            /// </summary>
            public TNextStep EndParameterizedReadVariant()
            {
                return _OnCompleted(CreateParameterizedReader());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class SetCommandStep
        {
            private readonly Param _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterizedReaderBuilder&lt;TInstance, TIdentifier, TParameter, TNextStep&gt;.SetCommandStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public SetCommandStep(Param parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Sets the read command.
            /// </summary>
            public SetMapperStep WithReadCommand(IComponentConfiguration<ICommand> command)
            {
                if (Object.ReferenceEquals(command, null))
                    throw new ArgumentNullException("command");
                _Parameters.ReadCommand = command;
                return new SetMapperStep(_Parameters);
            }

            /// <summary>
            /// Configures read command.
            /// </summary>
            public CommandBuilder<SetMapperStep>.CommandTypeAndTextStep ReadCommand()
            {
                return new CommandBuilder<SetMapperStep>.ResultGettingTypeStep(new CommandBuilder<SetMapperStep>.Parameters(c => WithReadCommand(c)))
                    .ExecuteAsReader();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class SetMapperStep
        {
            private readonly Param _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterizedReaderBuilder&lt;TInstance, TIdentifier, TParameter, TNextStep&gt;.SetMapperStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public SetMapperStep(Param parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Withes the parameters mapper.
            /// </summary>
            /// <param name="mapper">The mapper.</param>
            public SetIdentifierProviderStep WithParametersMapper(IComponentConfiguration<IMapper<TParameter>> mapper)
            {
                if (Object.ReferenceEquals(mapper, null))
                    throw new ArgumentNullException("mapper");
                _Parameters.ParameterMapper = mapper;
                return new SetIdentifierProviderStep(_Parameters);
            }

            /// <summary>
            /// Maps parameter using delegate that returns value by name.
            /// </summary>
            public SetIdentifierProviderStep DelegateMapper(Func<TParameter, string, IRegistry, object> mapParameter)
            {
                if (Object.ReferenceEquals(mapParameter, null))
                    throw new ArgumentNullException("mapParameter");

                return WithParametersMapper(new DelegateMapperConfiguration<TParameter>
                {
                    GetObjectDataFunction = mapParameter,
                    ApplyObjectDataFunction = (data, entity, registry) => { }
                });
            }

            /// <summary>
            /// Maps the parameters using reflection.
            /// </summary>
            public SetIdentifierProviderStep MapParametersUsingReflection()
            {
                return WithParametersMapper(Component.Mapper.Reflection<TParameter>());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class SetIdentifierProviderStep
        {
            private readonly Param _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterizedReaderBuilder&lt;TInstance, TIdentifier, TParameter, TNextStep&gt;.SetIdentifierProviderStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public SetIdentifierProviderStep(Param parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Sets the identifier provider.
            /// </summary>
            /// <param name="identifierProvider">The identifier provider.</param>
            /// <returns></returns>
            public EndStep WithIdentifierProvider(IComponentConfiguration<IIdentifierProvider<TIdentifier>> identifierProvider)
            {
                if (Object.ReferenceEquals(identifierProvider, null))
                    throw new ArgumentNullException("identifierProvider");
                _Parameters.IdentifierProvider = identifierProvider;
                return new EndStep(_Parameters);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class EndStep
        {
            private readonly Param _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterizedReaderBuilder&lt;TInstance, TIdentifier, TParameter, TNextStep&gt;.EndStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public EndStep(Param parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Ends the parameterized read variant.
            /// </summary>
            public TNextStep EndReader()
            {
                return _Parameters.EndParameterizedReadVariant();
            }
        }
    }

    /// <summary>
    /// Contains set of classes are required to build default read variant.
    /// </summary>
    public static class ParameterlessReaderBuilder<TInstance, TIdentifier, TNextStep>
        where TInstance : class
    {
        /// <summary>
        /// Parameters are required to create instance of default read variant configuration.
        /// </summary>
        public class Params
        {
            private readonly Func<ParameterlessReaderConfiguration<TInstance, TIdentifier>, TNextStep> _OnCompleted;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterlessReaderBuilder&lt;TInstance, TIdentifier, TNextStep&gt;.Params"/> class.
            /// </summary>
            /// <param name="onCompleted">The on completed.</param>
            public Params(Func<ParameterlessReaderConfiguration<TInstance, TIdentifier>, TNextStep> onCompleted)
            {
                if (Object.ReferenceEquals(onCompleted, null))
                    throw new ArgumentNullException("onCompleted");
                _OnCompleted = onCompleted;
            }

            /// <summary>
            /// Gets or sets a value indicating whether [use resultset for object construction].
            /// </summary>
            /// <value>
            /// 	<c>true</c> if [use resultset for object construction]; otherwise, <c>false</c>.
            /// </value>
            public bool UseResultsetForObjectConstruction { get; set; }

            /// <summary>
            /// Gets or sets the read command.
            /// </summary>
            /// <value>The read command.</value>
            public IComponentConfiguration<ICommand> ReadCommand { get; set; }

            /// <summary>
            /// Gets or sets the identifier provider.
            /// </summary>
            /// <value>The identifier provider.</value>
            public IComponentConfiguration<IIdentifierProvider<TIdentifier>> IdentifierProvider { get; set; }

            private ParameterlessReaderConfiguration<TInstance, TIdentifier> CreateReader()
            {
                return new ParameterlessReaderConfiguration<TInstance, TIdentifier>
                {
                    IdentifierProvider = IdentifierProvider,
                    ReadCommand = ReadCommand,
                    UseResultSetForObjectConstruction = UseResultsetForObjectConstruction
                };
            }

            /// <summary>
            /// Completes this instance.
            /// </summary>
            /// <returns></returns>
            public TNextStep Complete()
            {
                return _OnCompleted(CreateReader());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class SetCommandStep
        {
            private readonly Params _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterlessReaderBuilder&lt;TInstance, TIdentifier, TNextStep&gt;.SetCommandStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public SetCommandStep(Params parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Sets the read command.
            /// </summary>
            public SetIdentifierProviderStep WithCommand(IComponentConfiguration<ICommand> readCommand)
            {
                if (Object.ReferenceEquals(readCommand, null))
                    throw new ArgumentNullException("readCommand");
                _Parameters.ReadCommand = readCommand;
                return new SetIdentifierProviderStep(_Parameters);
            }

            /// <summary>
            /// Configures read command.
            /// </summary>
            public CommandBuilder<SetIdentifierProviderStep>.CommandTypeAndTextStep ReadCommand()
            {
                return new CommandBuilder<SetIdentifierProviderStep>.ResultGettingTypeStep(
                            new CommandBuilder<SetIdentifierProviderStep>.Parameters(c => WithCommand(c)))
                    .ExecuteAsReader();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public class SetIdentifierProviderStep
        {
            private readonly Params _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterlessReaderBuilder&lt;TInstance, TIdentifier, TNextStep&gt;.SetIdentifierProviderStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public SetIdentifierProviderStep(Params parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Sets the identifier provider.
            /// </summary>
            public EndStep WithIdentifierProvider(IComponentConfiguration<IIdentifierProvider<TIdentifier>> identifierProvider)
            {
                if (Object.ReferenceEquals(identifierProvider, null))
                    throw new ArgumentNullException("identifierProvider");
                _Parameters.IdentifierProvider = identifierProvider;
                return new EndStep(_Parameters);
            }
        }

        /// <summary>
        /// Completes default read variant.
        /// </summary>
        public class EndStep
        {
            private readonly Params _Parameters;

            /// <summary>
            /// Initializes a new instance of the <see cref="ParameterlessReaderBuilder&lt;TInstance, TIdentifier, TNextStep&gt;.EndStep"/> class.
            /// </summary>
            /// <param name="parameters">The parameters.</param>
            public EndStep(Params parameters)
            {
                if (Object.ReferenceEquals(parameters, null))
                    throw new ArgumentNullException("parameters");
                _Parameters = parameters;
            }

            /// <summary>
            /// Ends the default read variant.
            /// </summary>
            /// <returns></returns>
            public TNextStep EndReader()
            {
                return _Parameters.Complete();
            }
        }
    }

}
